﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using System.Collections;

namespace Microsoft.Office.OneNote
{
    internal class OneNoteObjectStorage<TStored> : OneNoteObjectStorageBase
        where TStored : OneNoteObjectWithIdentity
    {
        private readonly List<TStored> _children = new List<TStored>();

        public void UpdateFrom(IEnumerable<XElement> elements)
        {
            var missing = new List<TStored>(_children);
            int i = 0;

            foreach (XElement element in elements)
            {
                UpdateStoredElement(missing, ref i, element);
            }

            // invalidate objects that disappeared since last update

            foreach (TStored obj in missing)
            {
                obj.MarkInvalid();
                _children.Remove(obj);
            }

            RaiseUpdated();
        }

        public bool Contains(TStored obj)
        {
            return _children.Contains(obj);
        }

        public TStored this[OneNoteObjectId id]
        {
            get
            {
                if (id == OneNoteObjectId.Empty)
                {
                    throw new ArgumentException();
                }

                return _children.SingleOrDefault(obj => obj.ObjectId == id); 
            }
        }

        protected sealed override ICollection GetCollection()
        {
            return _children;
        }

        private void UpdateStoredElement(List<TStored> missing, ref int i, XElement element)
        {
            OneNoteObjectId id = OneNoteSchema.GetObjectID(element);

            if (id == OneNoteObjectId.Empty)
            {
#if THROW_ON_UNKNOWN_ELEMENTS
                throw OneNoteException.UnknownXmlElement(elementName);
#else
#warning        Should throw an error here!
                return;
#endif
            }

            TStored obj = this[id];

            if (obj == null)
            {
                // a new object

                obj = (TStored)OneNoteObjectFactory.CreateWithIdentity(element.Name, id);

                InitializeStoredObject(obj);
                _children.Insert(i, obj);
                i++;
            }
            else
            {
                // an existing object

                missing.Remove(obj);
            }

            OneNoteSchema.Deserialize(obj, element);
        }

        protected virtual void InitializeStoredObject(TStored obj)
        {
        }
    }
}
